using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;

namespace Client
{
    abstract class ReceiveListener
    {
        public abstract void receive(String json);
    }
    class TcpConnection
    {
        public delegate void SuccessDelegate(Response response);


        Dictionary<int, SuccessDelegate> requestList = new Dictionary<int, SuccessDelegate>();

        private static TcpConnection c = new TcpConnection();

        Socket socketClient;
        Thread threadClient;

        public Boolean isConnected = false;
        //static void Main(string[] args)
        //{
        //    new TcpConnection().handshake("{\"cmd\": \"login\", \"version\": \"11\", \"seq\": 2, \"cmd_body\": {\"username\": \"11\",\"password\": \"8888\"}}");
        //    Console.ReadLine();
        //}
        public static TcpConnection getInstance()
        {
            return c;
        }

        public Boolean connected()
        {
            return isConnected;
        }

        public void handshake(SuccessDelegate success)
        {
            if (c.socketClient != null)
            {
                LogHelper.info("Close prevous connection.");
                try
                {
                    c.socketClient.Shutdown(SocketShutdown.Both);
                    c.socketClient.Close();
                }
                catch (Exception e)
                {
                }
                c.threadClient = null;
            }
            LogHelper.info("Connect to server [IP:" + Global.IP + ", PORT:" + Global.PORT + "]...");

            try {
                c.handshake(Global.IP, Global.PORT, success);
            }
            catch (Exception e)
            {
                Response response = new Response();
                response.code = 999;
                response.msg = "服务器连接失败，请联系联系人";
                success(response);
            }
        }


        public void loop()
        {

            //创建一个线程 用于监听服务端发来的消息
            threadClient = new Thread(RecMsg);

            //将窗体线程设置为与后台同步
            threadClient.IsBackground = true;

            //启动线程
            threadClient.Start();
        }
        #region 连接服务端方法
        public void handshake(string IP, string Port, SuccessDelegate success)
        {
            isConnected = false;
            //定义一个套字节监听  包含3个参数(IP4寻址协议,流式连接,TCP协议)
            socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            //需要获取文本框中的IP地址
            IPAddress ipaddress = IPAddress.Parse(IP);
            //将获取的ip地址和端口号绑定到网络节点endpoint上
            IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(Port));

            //这里客户端套接字连接到网络节点(服务端)用的方法是Connect 而不是Bind
            socketClient.Connect(endpoint);

            loop();

            Request request = Global.getHandshakeJson();
            String json = JSONWriter.ToJson(request);
            byte[] data = Encoding.UTF8.GetBytes(json);
            Int32 len = data.Length;
            

            byte[] length = System.BitConverter.GetBytes(len);
            Array.Reverse(length);
            byte[] byteData = new byte[length.Length + data.Length];
            BitConverter.ToString(data);
            length.CopyTo(byteData, 0);
            data.CopyTo(byteData, length.Length);
            //调用客户端套接字发送字节数组
            socketClient.Send(byteData);
            LogHelper.info("[Send]" + JSONWriter.ToJson(request));
            requestList[request.seq] = success;
        }
        #endregion

        public static string UrlEncode(string str)
        {
            StringBuilder sb = new StringBuilder();
            byte[] byStr = Encoding.BigEndianUnicode.GetBytes(str); //默认是System.Text.Encoding.Default.GetBytes(str)
            for (int i = 0; i < byStr.Length; i++)
            {
                sb.Append(@"%" + Convert.ToString(byStr[i], 16));
            }

            return (sb.ToString());
        }

        public void clientResponseMsg(Response response)
        {
            String json = JSONWriter.ToJson(response);

            byte[] data = Encoding.UTF8.GetBytes(json);
            Int32 len = data.Length;
            byte[] length = System.BitConverter.GetBytes(len);
            Array.Reverse(length);
            byte[] byteData = new byte[length.Length + data.Length];
            length.CopyTo(byteData, 0);
            data.CopyTo(byteData, length.Length);
            //调用客户端套接字发送字节数组
            socketClient.Send(byteData);
        }
        #region 发送节目全屏byte串信息到服务端的方法
        public void clientSendMsg(Request request, SuccessDelegate success)
        {
            if (!isConnected)
            {
                Response response =new Response();
                response.code = 999;
                response.msg = "正在连接服务器,请稍后重试";
                success(response);
                return;
            }
            String json = JSONWriter.ToJson(request);
            byte[] data = Encoding.UTF8.GetBytes(json);
            Int32 len = data.Length;
            byte[] length = System.BitConverter.GetBytes(len);
            Array.Reverse(length);
            byte[] byteData = new byte[length.Length + data.Length];
            length.CopyTo(byteData, 0);
            data.CopyTo(byteData, length.Length);
            //调用客户端套接字发送字节数组
            socketClient.Send(byteData);
            LogHelper.info("[Send]" + JSONWriter.ToJson(request));
            requestList[request.seq] = success;
        }
        #endregion

        #region 接收服务端发来信息的方法
        private void RecMsg()
        {
            while (true) //持续监听服务端发来的消息
            {
                try { 
                    //定义一个1024*200的内存缓冲区 用于临时性存储接收到的信息
                    byte[] header = new byte[4];
                    //将客户端套接字接收到的数据存入内存缓冲区, 并获取其长度
                    int l = c.socketClient.Receive(header);

                    if (l == 0)
                    {
                        LogHelper.info("Connection closed.");
                        //handshake((response) =>
                        //{
                        //    //链接成功
                        //    LogHelper.info("Connect successed.");
                        //});

                        c.isConnected = false;
                        socketClient = null;
                        threadClient = null;
                        try
                        {
                            LogHelper.info("[Receive] 服务器断开连接");
                        }
                        catch (Exception e2)
                        {
                            //MessageBox.Show("服务器断开连接.");
                        }
                        MessageBox.Show("服务器断开连接.");
                        Application.Exit();
                        return;
                    }

                    Array.Reverse(header);
                    int length = System.BitConverter.ToInt32(header, 0);


                    byte[] body = new byte[length];
                    int bl = c.socketClient.Receive(body);

                    String jsonBody = Encoding.ASCII.GetString(body);

                    jsonBody = unicode_js_1(jsonBody);

                    Response response1 = jsonBody.FromJson<Response>();


                    if (response1.seq % 2 == 1)
                    {
                        //这是请求
                        Request request = jsonBody.FromJson<Request>();
                        if (request.cmd == "upload_config")
                        {
                            upload_config(request);
                        }
                        else if (request.cmd == "upload_screenshots")
                        {
                            upload_screenshots(request);
                        }
                        else if (request.cmd == "unlock")
                        {
                            Response response = new Response();
                            response.seq = request.seq + 1;
                            clientResponseMsg(response);
                            System.Environment.Exit(0);
                        }
                        else if (request.cmd == "upload_log")
                        {
                            upload_log(request);
                        }
                        else if (request.cmd == "update_config")
                        {
                            update_config(request);
                        }
                    }
                    else
                    {
                        LogHelper.info("[Receive]" + jsonBody);
                        if (requestList.ContainsKey(response1.seq - 1))
                        {
                            SuccessDelegate success = requestList[response1.seq - 1];
                            requestList.Remove(response1.seq - 1);
                            success(response1);
                        }
                    }
                }
                catch (SocketException e)
                {
                    c.isConnected = false;
                    socketClient = null;
                    threadClient = null;
                    try {
                        LogHelper.info("[Receive] 出现异常，断开连接," + e.ToString());
                    }
                    catch (Exception e2)
                    {
                        //MessageBox.Show("服务器断开连接.");
                    }
                    MessageBox.Show("出现异常，断开连接.");
                    Application.Exit();
                    return;
                }
            }
        }
        #endregion
        /// <summary>
        /// unicode转中文（符合js规则的）
        /// </summary>
        /// <returns></returns>
        public static string unicode_js_1(string str)
        {
            string outStr = "";
            Regex reg = new Regex(@"(?i)\\u([0-9a-f]{4})");
            outStr = reg.Replace(str, delegate(Match m1)
            {
                return ((char)Convert.ToInt32(m1.Groups[1].Value, 16)).ToString();
            });
            return outStr;
        }
        public void update_config(Request request)
        {
            Global.configs = (Dictionary<string, object>)request.cmd_body["config"];
            Response response = new Response();
            response.seq = request.seq + 1;
            clientResponseMsg(response);
            MessageBox.Show("变更配置通知,读取本地配置可更新到文本框");
        }
        public void upload_config(Request request)
        {
            Response response = new Response();
            response.seq = request.seq + 1;
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("configs", Global.configs);
            response.resp_body = dict;
            clientResponseMsg(response);
        }
        public void upload_log(Request request)
        {
            String level = request.cmd_body["level"].ToString();
            String data_str = request.cmd_body["date_str"].ToString();
            String fileName = LogHelper.getFileName(level, data_str);

            Response response = new Response();
            response.seq = request.seq + 1;
            Dictionary<string, object> dict = new Dictionary<string, object>();
            if (File.Exists("logs/"+fileName))
            {
                Int64 time = Global.GetTimeStamp();
                dict.Add("base64file", textToBase64String("logs/"+fileName));
                dict.Add("fileName", fileName +"."+time);
                dict.Add("module", "client_log");
                response.resp_body = dict;
            }
            else
            {
                response.code = 2;
                response.msg = "File not exists.";
            }
            clientResponseMsg(response);
        }
        public void upload_screenshots(Request request)
        {
            Image baseImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
            Graphics g = Graphics.FromImage(baseImage);
            g.CopyFromScreen(new Point(0, 0), new Point(0, 0), Screen.AllScreens[0].Bounds.Size);
            g.Dispose();
            Int64 time = Global.GetTimeStamp();
            String fileName = "screenshots_" + time + ".jpg";
            baseImage.Save(fileName, ImageFormat.Jpeg);

            Response response = new Response();
            response.seq = request.seq + 1;
            Dictionary<string, object> dict = new Dictionary<string, object>();
            dict.Add("base64file", ImgToBase64String(fileName));
            dict.Add("fileName", fileName);
            dict.Add("module", "client_screenshots");
            dict.Add("accuracy", request.cmd_body["accuracy"]);
            response.resp_body = dict;
            clientResponseMsg(response);
        }

        private String textToBase64String(String fileName)
        {
            System.IO.FileStream fs = System.IO.File.OpenRead(fileName);//传文件的路径即可
            System.IO.BinaryReader br = new BinaryReader(fs);
            byte[] bt = br.ReadBytes(Convert.ToInt32(fs.Length));
            string base64String = Convert.ToBase64String(bt);
            br.Close();
            fs.Close();
            return base64String;
        }

        //图片 转为    base64编码的文本
        private String ImgToBase64String(string Imagefilename)
        {
            try
            {
                if (!Directory.Exists("tempfile"))
                {
                    Directory.CreateDirectory("tempfile");
                }
                Bitmap bmp = new Bitmap(Imagefilename);
                FileStream fs = new FileStream("tempfile/"+Imagefilename + ".txt", FileMode.Create);
                StreamWriter sw = new StreamWriter(fs);

                MemoryStream ms = new MemoryStream();
                bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                byte[] arr = new byte[ms.Length];
                ms.Position = 0;
                ms.Read(arr, 0, (int)ms.Length);
                ms.Close();
                String strbaser64 = Convert.ToBase64String(arr);
                sw.Write(strbaser64);

                sw.Close();
                fs.Close();
                // MessageBox.Show("转换成功!");
                return strbaser64;
            }
            catch (Exception ex)
            {
                MessageBox.Show("ImgToBase64String 转换失败\nException:" + ex.Message);
            }
            return null;
        }
    }
}